package com.module.base.ext

import android.database.sqlite.SQLiteCantOpenDatabaseException
import android.database.sqlite.SQLiteException
import android.text.TextUtils
import androidx.lifecycle.viewModelScope
import com.module.base.base.BaseExceptionViewModel
import com.module.base.network.ERROR
import com.module.base.network.exception.ServerException
import com.module.base.network.listener.IExtendServerException
import com.module.base.utils.LogUtils
import com.google.gson.JsonParseException
import com.google.gson.JsonSerializer
import com.module.frame.base.BaseFrameViewModel
import com.module.frame.ext.loadDialogShareFlow
import com.module.frame.ext.postEvent
import com.module.frame.retrofit.BaseResponse
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.apache.http.conn.ConnectTimeoutException
import org.json.JSONException
import retrofit2.HttpException
import java.io.IOException
import java.io.NotSerializableException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.text.ParseException
import java.util.regex.Pattern
import javax.net.ssl.SSLHandshakeException

/**
 * @param request  请求返回体为BaseResponse<T>           sucResult返回T      常用接口请求
 * @param request2 请求返回体为<T>                       sucResult返回T      常用于自定义线程处理
 * @param request3 请求返回体为BaseResponse<T>           sucResult返回MutableSharedFlow<T?>      常用接口请求,然后不用处理逻辑，直接发送SharedFlow消息出去
 * @param requestFlow 请求返回体为Flow<BaseResponse<T>   sucResult返回T      常用Flow
 *
 *
 * @param loadShareFlow 加载弹窗需要主要，如果是ViewModel调用  默认使用的ViewModel里面的加载弹窗，局部的
 *                                     如果非ViewModel调用  则默认使用全局的 @see com.module.frame.ext.loadDialogShareFlow
 *
 * 不使用runCatching来捕捉异常，不然挂起函数那边无法跳过异常，捕捉也没有用
 * 常用于
 */


/**
 * 超时时间
 */
private const val TIMEOUT_TIME: Long = 30000


fun <T> BaseExceptionViewModel.request(
    block: suspend () -> BaseResponse<T>,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s
): Job {
    return request(
        block,
        sucResult,
        failResult,
        isShowDialog,
        viewModelScope,
        timeMillis,
        loadDialogShareFlow,
        this
    )
}

/**
 * //请求成功失败返回 直接返回数据BaseResponse
 * @param scope 默认使用全局的作用域
 * @param block 请求体方法
 * @param isShowDialog 是否显示加载框
 * @param sucResult 请求成功
 * @param failResult 请求失败
 * @param timeMillis 超时时间
 * @param loadShareFlow 加载框通知
 * @param extendException 额外的异常处理
 */
fun <T> request(
    block: suspend () -> BaseResponse<T>,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    scope: CoroutineScope = GlobalScope,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s
    loadShareFlow: MutableSharedFlow<Boolean> = loadDialogShareFlow,//加载弹窗,默认使用全局的加载弹窗
    extendException: IExtendServerException? = null,//额外的异常处理
): Job {
    return scope.launch(Dispatchers.Main) {
        try {
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                true
            )
            var it: BaseResponse<T>
            withTimeout(timeMillis) {
                //请求体
                it = block()
            }
            //成功处理
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            try {
                if (it.isSuccess()) {
                    sucResult(it.getData())
                } else {
                    //处理异常
                    isDealException(extendException, it.getMsg(), it.getCode())?.apply {
                        failResult(this)
                    }
                }
            } catch (e: Exception) {
                //处理异常
                isDealException(extendException, e)?.apply {
                    failResult(this)
                }
            }
        } catch (it: Exception) {
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            //处理异常
            isDealException(extendException, it)?.apply {
                failResult(this)
            }
        }
    }
}

fun <T> BaseFrameViewModel.request2(
    block: suspend () -> T,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    timeMillis: Long = TIMEOUT_TIME,//默认超时20s
): Job {
    return request2(
        block,
        sucResult,
        failResult,
        isShowDialog,
        viewModelScope,
        timeMillis,
        loadDialogShareFlow
    )
}

/**
 * //请求成功失败返回 直接返回数据
 * @param scope 默认使用全局的作用域
 * @param block 请求体方法
 * @param isShowDialog 是否显示加载框
 * @param sucResult 请求成功
 * @param failResult 请求失败
 * @param timeMillis 超时时间
 * @param loadShareFlow 加载框通知
 */
fun <T> request2(
    block: suspend () -> T,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    scope: CoroutineScope = GlobalScope,
    timeMillis: Long = TIMEOUT_TIME,//默认超时20s
    loadShareFlow: MutableSharedFlow<Boolean> = loadDialogShareFlow,//加载弹窗
    extendException: IExtendServerException? = null,//额外的异常处理
): Job {
    return scope.launch(Dispatchers.Main) {
        try {
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                true
            )
            var it: T
            withTimeout(timeMillis) {
                //请求体
                it = block()
            }
            //成功处理
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            sucResult(it)
        } catch (it: Exception) {
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            //处理异常
            isDealException(extendException, it)?.apply {
                failResult(this)
            }
        }
    }
}


fun <T> BaseExceptionViewModel.request3(
    block: suspend () -> BaseResponse<T>,
    sucResult: MutableSharedFlow<T?>,//成功结果
    failResult: MutableSharedFlow<ServerException>,//失败结果
    isShowDialog: Boolean = true,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s

): Job {
    return request3(
        block,
        sucResult,
        failResult,
        isShowDialog,
        viewModelScope,
        timeMillis,
        loadDialogShareFlow,
        this
    )
}

/**
 * //请求成功失败返回 SharedFlow实例
 * @param scope 默认使用全局的作用域
 * @param block 请求体方法
 * @param isShowDialog 是否显示加载框
 * @param sucResult 请求成功
 * @param failResult 请求失败
 * @param timeMillis 超时时间
 * @param loadShareFlow 加载框通知
 * @param extendException 额外的异常处理
 * */
fun <T> request3(
    block: suspend () -> BaseResponse<T>,
    sucResult: MutableSharedFlow<T?>,//成功结果
    failResult: MutableSharedFlow<ServerException>,//失败结果
    isShowDialog: Boolean = true,
    scope: CoroutineScope = GlobalScope,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s
    loadShareFlow: MutableSharedFlow<Boolean> = loadDialogShareFlow,//加载弹窗
    extendException: IExtendServerException? = null,//额外的异常处理
): Job {
    return scope.launch(Dispatchers.Main) {
        try {
            var it: BaseResponse<T>
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                true
            )
            withTimeout(timeMillis) {
                //请求体
                it = block()
            }
            //成功处理
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            try {
                if (it.isSuccess()) {
                    postEvent(scope, sucResult, it.getData())
                } else {
                    //处理异常
                    isDealException(extendException, it.getMsg(), it.getCode())?.apply {
                        postEvent(scope, failResult, this)
                    }
                }
            } catch (e: Exception) {
                //处理异常
                isDealException(extendException, e)?.apply {
                    postEvent(scope, failResult, this)
                }
            }
        } catch (it: Exception) {
            if (isShowDialog && loadShareFlow != null) postEvent(
                scope,
                loadShareFlow,
                false
            )
            //处理异常
            isDealException(extendException, it)?.apply {
                postEvent(scope, failResult, this)
            }
        }
    }
}


fun <T> BaseExceptionViewModel.requestFlow(
    block: suspend () -> Flow<BaseResponse<T>>,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s
): Job {
    return requestFlow(
        block,
        sucResult,
        failResult,
        isShowDialog,
        viewModelScope,
        timeMillis,
        loadDialogShareFlow,
        this
    )
}

/**
 * //请求成功失败返回 返回Flow
 * @param scope 默认使用全局的作用域
 * @param block 请求体方法
 * @param isShowDialog 是否显示加载框
 * @param sucResult 请求成功
 * @param failResult 请求失败
 * @param timeMillis 超时时间
 * @param loadShareFlow 加载框通知
 * @param extendException 额外的异常处理
 * */
fun <T> requestFlow(
    block: suspend () -> Flow<BaseResponse<T>>,
    sucResult: (T?) -> Unit,
    failResult: (ServerException) -> Unit,
    isShowDialog: Boolean = true,
    scope: CoroutineScope = GlobalScope,
    timeMillis: Long = TIMEOUT_TIME,//默认超时10s
    loadShareFlow: MutableSharedFlow<Boolean> = loadDialogShareFlow,//加载弹窗
    extendException: IExtendServerException?,//额外的异常处理
): Job {
    return scope.launch(Dispatchers.Main) {
        var flow = block()
            .flowOn(Dispatchers.IO)
            .onStart {
                if (isShowDialog && loadShareFlow != null) postEvent(
                    scope,
                    loadShareFlow,
                    true
                )
            }
            .flowOn(Dispatchers.Main)
            .onEach {
                try {
                    if (it.isSuccess()) {
                        sucResult(it.getData())
                    } else {
                        //处理异常
                        isDealException(extendException, it.getMsg(), it.getCode())?.apply {
                            failResult(this)
                        }
                    }
                } catch (e: Exception) {
                    //处理异常
                    isDealException(extendException, e)?.apply {
                        failResult(this)
                    }
                }
            }
//            .catch {
//                isDealException(it)?.apply {
//                        failResult(this)
//                    }
//            }
            .onCompletion {
                //由于catch无法监听取消，所以不使用catch来捕捉异常，而是统一使用onCompletion来监听异常
                //如果为空则代表请求成功，不需要处理异常
                it?.let {
                    //处理异常
                    isDealException(extendException, it)?.apply {
                        failResult(this)
                    }
                }
                if (isShowDialog && loadShareFlow != null) postEvent(
                    scope,
                    loadShareFlow,
                    false
                )
            }
        withTimeout(timeMillis) {
            flow.collect()
        }
    }
}


/**
 * 报错重试
 * @param maxRetries 最高重试几次，不包含第一次请求
 * @param retryDelayMillis 重试等待时间
 */
suspend fun <T> retry(
    maxRetries: Int,
    retryDelayMillis: Long,
    block: suspend () -> T
): T {
    return try {
        block()
    } catch (e: Exception) {
        if (maxRetries > 0) {
            //延迟时间
            delay(retryDelayMillis)
            //重新调用
            retry(maxRetries - 1, retryDelayMillis, block)
        } else {
            throw e
        }
    }
}

/**
 * 需要跳过的code
 */
private val skipCodeList: MutableList<Int> by lazy {
    var list: MutableList<Int> = mutableListOf()
    //scope解绑取消不需要处理异常
    list.add(ERROR.JOB_SCOPE_CANCEL)
    list
}


/**
 * 异常处理
 */
private fun isDeal(e: Throwable): Boolean {
    var isDeal = true
    when (e) {
        is IllegalStateException -> {
            when {
                e.message?.equals("Job was cancelled") == true -> {
                    //由于JobCancellationException无法使用，所以只能判断Job was cancelled
                    //Job 的生命周期结束取消 所以不回调用，避免错误
                    isDeal = false
                }

            }

        }
    }
    return isDeal
}

private fun isDealException(e: Throwable): String? {
    if (!isDeal(e)) {
        return null
    }
    return e.message
}

/**
 * 接口异常
 */
private fun isDealException(
    extend: IExtendServerException?,
    message: String?,
    code: Int
): ServerException? {
    //优先处理拓展异常处理
    var ex = extend?.getExtendException(code)
    if (ex == null) {
        //然后在处理通用异常
        ex = ServerException(message, code)
    }
    if (skipCodeList.contains(ex.code)) {
        return null
    }
    if (!isDeal(ex)) {
        return null
    }
    return ex
}

/**
 * 抛出异常
 */
fun isDealException(extend: IExtendServerException?, e: Throwable): ServerException? {
    if (!isDeal(e)) {
        return null
    }
    //优先处理拓展异常处理
    var ex = extend?.getExtendException(e)
    if (ex == null) {
        //然后在处理通用异常
        ex = handleException(e)
    }
    if (skipCodeList.contains(ex.code)) {
        return null
    }
    return ex
}


/**
 * 异常处理
 */
fun handleException(e: Throwable): ServerException {
    val ex: ServerException
    when (e) {
        is HttpException -> {
            ex = ServerException(e, e.code())
            try {
                ex.msg = e.response()?.errorBody()?.string().toString()
            } catch (e1: IOException) {
                e1.printStackTrace()
                ex.msg = e1?.message.toString()
            }
        }
        is TimeoutCancellationException,
        is ConnectTimeoutException,
        is SocketTimeoutException -> {
            ex = ServerException(e, ERROR.TIMEOUT_ERROR)
            ex.msg = "网络连接超时，请检查网络"
        }
        is ConnectException -> {
            ex = ServerException(e, ERROR.TIMEOUT_ERROR)
            ex.msg = "网络异常，请检查网络"
        }
        is UnknownHostException -> {
            ex = ServerException(e, ERROR.TIMEOUT_ERROR)
            ex.msg = "网络异常，请检查网络"
        }
        is NullPointerException -> {
            ex = ServerException(e, ERROR.NULL_POINTER_EXCEPTION)
            ex.msg = "空指针异常"
        }
        is SSLHandshakeException -> {
            ex = ServerException(e, ERROR.SSL_ERROR)
            ex.msg = "证书验证失败"
        }
        is ClassCastException -> {
            ex = ServerException(e, ERROR.CAST_ERROR)
            ex.msg = "类型转换错误"
        }
        is JsonParseException,
        is JSONException,
        is JsonSerializer<*>,
        is NotSerializableException,
        is ParseException,
        -> {
            ex = ServerException(e, ERROR.PARSE_ERROR)
            ex.msg = "解析错误"
        }
        is IllegalStateException -> {
            ex = ServerException(e, ERROR.ILLEGAL_STATE_ERROR)
            ex.msg = e.message.toString()
            //数据库迁移报错
            when {
                ex.msg?.contains("Please provide the necessary Migration path via RoomDatabase.Builder") == true -> {
                    //更换code
                    ex.msg = "当前数据库版本过低，请更新app最新版本"
                    ex.code = ERROR.ROOM
                }
                ex.msg?.equals("Job was cancelled") == true -> {
                    //由于JobCancellationException无法使用，所以只能判断Job was cancelled
                    //更换code
                    ex.msg = "Job 的生命周期结束取消"
                    ex.code = ERROR.JOB_SCOPE_CANCEL
                }
                ex.msg?.equals("StandaloneCoroutine was cancelled") == true -> {
                    //由于JobCancellationException无法使用，所以只能判断StandaloneCoroutine was cancelled
                    //更换code
                    ex.msg = "取消"
                    ex.code = ERROR.JOB_CANCEL
                }
            }

        }
        is SQLiteCantOpenDatabaseException,
        is android.database.SQLException,
        is java.sql.SQLException,
        is SQLiteException
        -> {
            //无法打开数据库
            ex = ServerException(e, ERROR.DATABASE_OPEN)
            ex.msg = "无法打开数据库"
        }
        else -> {
            ex = ServerException(e, ERROR.UNKNOWN)
            ex.msg = if (checkContainName(e.message.toString())) e.message.toString() else "未知错误"
        }
    }
    ex.e = e
    LogUtils.e("异常：${ex.msg}")
    return ex
}


/**
 * 判断是否存在汉字
 */
private fun checkContainName(countname: String): Boolean {
    if (TextUtils.isEmpty(countname)) {
        return false
    }
    val p = Pattern.compile("[\u4e00-\u9fa5]")
    val m = p.matcher(countname)
    return m.find()
}

/**
 * 判断整个字符串都由汉字组成
 */
private fun checkName(name: String): Boolean {
    if (TextUtils.isEmpty(name)) {
        return false
    }
    var n = 0
    for (element in name) {
        n = element.toInt()
        if (n !in 19968..40868) {
            return false
        }
    }
    return true
}